#include "AspectEngine.h"
#include <Vector>

namespace agtk {

FontData::FontData(const char* path, unsigned int height, float kerning)
{
	// Allocate some memory to store the texture ids.
	m_textures = new GLuint[128];

	m_height = height;

	m_kerning = kerning;

	// Create and initilize a freetype font library.
	FT_Library library;
	if (FT_Init_FreeType( &library )) 
		g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "Could not initialize FreeType.");

	// The object in which Freetype holds information on a given font is called a face.
	FT_Face face;

	// This is where we load in the font information from the file.
	// This is the most likely location for runtime errors.
	if (FT_New_Face( library, path, 0, &face )) 
		g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "FreeType new face failure. There may be an issue with your font file.");

	// For some twisted reason, Freetype measures font size in terms of 1/64ths of pixels. 
	// Thus, to make a font h pixels high, we need to request a size of h*64.
	FT_Set_Char_Size( face, height*64, height*64, 96, 96);

	// Here we ask opengl to allocate resources for all the textures and displays lists which we are about to create.  
	m_listBase = glGenLists(128);
	glGenTextures( 128, m_textures );

	// This is where we actually create each of the fonts display lists.
	for (unsigned char i=0;i<128;i++)
		BuildDisplayList(face, i, m_listBase, m_textures);

	// We don't need the face information now that the display lists have been created, so we free the assosiated resources.
	FT_Done_Face(face);

	// Ditto for the library.
	FT_Done_FreeType(library);
}

FontData::~FontData(void)
{
	glDeleteLists(m_listBase,128);
	glDeleteTextures(128,m_textures);
	delete [] m_textures;
}

FontData* LoadFont(const char* path, unsigned int height, float kerning)
{
	return new FontData(path, height, kerning);
}

int FontData::NextPowerOf2(int param)
{
	int rval=2;
	while(rval<param) rval<<=1;
	return rval;
}

void FontData::BuildDisplayList(FT_Face face, char ch, GLuint listBase, GLuint *texBase)
{
	// Load the Glyph for our character.
	if(FT_Load_Glyph( face, FT_Get_Char_Index( face, ch ), FT_LOAD_DEFAULT ))
		g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "FreeType glyph load failure.");

	// Move the face's glyph into a Glyph object.
    FT_Glyph glyph;
    if(FT_Get_Glyph( face->glyph, &glyph ))
		g_aspectEngine->GetLogger()->Log(LOGGER_ERROR, "FreeType get glyph failure.");

	// Convert the glyph to a bitmap.
	FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
    FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

	// This reference will make accessing the bitmap easier
	FT_Bitmap& bitmap=bitmap_glyph->bitmap;

	// Use our helper function to get the widths of the bitmap data that we will need in order to create our texture.
	int width = NextPowerOf2( bitmap.width );
	int height = NextPowerOf2( bitmap.rows );

	// Allocate memory for the texture data.
	GLubyte* expanded_data = new GLubyte[ 2 * width * height];

	// Here we fill in the data for the expanded bitmap.
	// Notice that we are using two channel bitmap (one for luminocity and one for alpha).
	// We assign both luminocity and alpha to the value that we find in the FreeType bitmap. 
	// We use the ?: operator so that value which we use will be 0 if we are in the padding zone, and whatever is the the Freetype bitmap otherwise.
	for(int j=0; j < height;j++) for(int i=0; i < width; i++) {
        expanded_data[2*(i+j*width)] = 255;
        expanded_data[2*(i+j*width)+1] =
                (i>=bitmap.width || j>=bitmap.rows) ?
                0 : bitmap.buffer[i + bitmap.width*j];
	}

	// Now we just setup some texture paramaters.
    glBindTexture( GL_TEXTURE_2D, texBase[ch]);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

	// Here we actually create the texture itself, notice that we are using GL_LUMINANCE_ALPHA to indicate that we are using 2 channel data.
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height,
		  0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );

	// With the texture created, we don't need to expanded data anymore.
    delete [] expanded_data;

	// So now we can create the display list.
	glNewList(listBase+ch,GL_COMPILE);

	glBindTexture(GL_TEXTURE_2D,texBase[ch]);

	// First we need to move over a little so that the character has the right amount of space between it and the one before it.
	glTranslatef(bitmap_glyph->left+m_kerning,0,0);

	// Now we move down a little in the case that the bitmap extends past the bottom of the line (this is only true for characters like 'g' or 'y').
	glPushMatrix();
	glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

	// Now we need to account for the fact that many of our textures are filled with empty padding space.
	// We figure what portion of the texture is used by the actual character and store that information in the x and y variables, then when we draw the quad, we will only reference the parts of the texture that we contain the character itself.
	float x=(float)bitmap.width / (float)width;
	float y=(float)bitmap.rows / (float)height;

	// Here we draw the texturemaped quads.
	// The bitmap that we got from FreeType was not oriented quite like we would like it to be, so we need to link the texture to the quad so that the result will be properly aligned.
	glBegin(GL_QUADS);
	glTexCoord2d(0,0); glVertex2f(0,bitmap.rows);
	glTexCoord2d(0,y); glVertex2f(0,0);
	glTexCoord2d(x,y); glVertex2f(bitmap.width,0);
	glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows);
	glEnd();
	glPopMatrix();
	glTranslatef(face->glyph->advance.x >> 6 ,0,0);

	// Finish the display list.
	glEndList();

	// Set width of the glyph.
	m_glyphWidths[ch] = face->glyph->advance.x >> 6;

	// Free the Glyph.
	FT_Done_Glyph(glyph);
}

void PushScreenCoordinateMatrix()
{
	glPushAttrib(GL_TRANSFORM_BIT);
	GLint	viewport[4];
	glGetIntegerv(GL_VIEWPORT, viewport);
	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]);
	glPopAttrib();
}

void PopProjectionMatrix()
{
	glPushAttrib(GL_TRANSFORM_BIT);
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glPopAttrib();
}

void Print(FontData* ft_font, float x, float y, const char *fmt, Color &color) 
{
	// Set color.
	glColor4f(color.r, color.g, color.b, color.a);
	
	// We want a coordinate system where things coresponding to window pixels.
	PushScreenCoordinateMatrix();					
	
	GLuint font=ft_font->m_listBase;

	//We make the height about 1.5*.
	float h=ft_font->m_height/.63f;
	
	// Holds the string.
	char text[256];

	// Pointer To List Of Arguments.
	va_list	ap;

	// If there is no text, don't do anything.
	if (fmt == NULL) {
		*text=0;
	} else {
		// Parses The String For Variables.
		va_start(ap, fmt);
		// And Converts Symbols To Actual Numbers.
	    vsprintf(text, fmt, ap);
		// Results Are Stored In Text
		va_end(ap);
	}

	// Here is some code to split the text that we have been given into a set of lines.
	const char *start_line=text;
	std::vector<std::string> lines;

	const char * c = text;

	for(;*c;c++) {
		if(*c=='\n') {
			std::string line;
			for(const char *n=start_line;n<c;n++) line.append(1,*n);
			lines.push_back(line);
			start_line=c+1;
		}
	}
	if(start_line) {
		std::string line;
		for(const char *n=start_line;n<c;n++) line.append(1,*n);
		lines.push_back(line);
	}

	glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT  | GL_ENABLE_BIT | GL_TRANSFORM_BIT);	
	glMatrixMode(GL_MODELVIEW);
	glDisable(GL_LIGHTING);
	glEnable(GL_TEXTURE_2D);
	glDisable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);	

	glListBase(font);

	float modelview_matrix[16];	
	glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix);

	// This is where the text display actually happens.
	// For each line of text we reset the modelview matrix so that the line's text will start in the correct position.
	// Notice that we need to reset the matrix, rather than just translating down by h. This is because when each character is draw it modifies the current matrix so that the next character will be drawn immediatly after it.  
	for(unsigned int i=0;i<lines.size();i++) {
		glPushMatrix();
		glLoadIdentity();
		glTranslatef(x,y-h*i,0);
		glMultMatrixf(modelview_matrix);
		glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());
		glPopMatrix();
	}

	glPopAttrib();		

	PopProjectionMatrix();

	// Reset color.
	glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
}

int MeasureLength(FontData* ft_font, const char *fmt) 
{
	//We make the height about 1.5*.
	float h=ft_font->m_height/.63f;
	
	// Holds the string.
	char text[256];

	// Pointer To List Of Arguments.
	va_list	ap;

	// If there is no text, don't do anything.
	if (fmt == NULL) {
		return 0;
	} else {
		// Parses The String For Variables.
		va_start(ap, fmt);
		// And Converts Symbols To Actual Numbers.
	    vsprintf(text, fmt, ap);
		// Results Are Stored In Text
		va_end(ap);
	}

	// Here is some code to split the text that we have been given into a set of lines.
	const char *start_line=text;
	std::vector<std::string> lines;

	const char * c = text;

	for(;*c;c++) {
		if(*c=='\n') {
			std::string line;
			for(const char *n=start_line;n<c;n++) line.append(1,*n);
			lines.push_back(line);
			start_line=c+1;
		}
	}
	int length = 0;
	if(start_line) {
		for(const char *n=start_line;n<c;n++) length += (ft_font->m_glyphWidths[*n] + ft_font->m_kerning);
	}

	return length;
}

}